package controller;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import javax.swing.JFileChooser;
import javax.swing.JSlider;
import model.Instrument;
import model.Piece;
import model.Player;
import model.Sequence;
import net.beadsproject.beads.core.AudioContext;
import view.*;
import view.tool.EffectsTool;
import view.tool.SequenceTool;
import view.tool.ToolType;

/**
 * Listens for events that want to change the current Piece being used in the program.
 */
public class ChangePieceListener implements ActionListener {

	//ActionCommands that this listener can act on.
	public static final String
		NEW = "NEW", LOAD = "LOAD",
		CHANGE_LENGTH = "CHANGE_LENGTH", RESET_LENGTH = "RESET_LENGTH";

	private GUI gui;
	private Player currentPlayer;

	public ChangePieceListener(Piece initialPiece, GUI gui) {
		this.gui = gui;
		this.changeCurrentPiece(initialPiece); //Set GUI to show initial piece.
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		String command = e.getActionCommand();

		if(command.equals(NEW)) {
			//Set up a new piece
			this.changeCurrentPiece(Piece.createNewPiece("Untitled", 20));
			gui.showToolPanel(ToolType.EFFECTS);
		} else if(command.equals(LOAD)) {
			//Load up a selected piece using an open file dialogue
			JFileChooser chooser = new JFileChooser();
			chooser.setDialogTitle("Load Piece");
			int ret = chooser.showOpenDialog(gui);
			if(ret != JFileChooser.APPROVE_OPTION) return;

			File selectedFile = chooser.getSelectedFile();

			this.changeCurrentPiece(this.loadPiece(selectedFile));
			gui.collapseToolPanel();
		} else if(command.equals(CHANGE_LENGTH)) {
			//Create a new Piece which is identical to the current piece only with
			//a different length.
			this.changeCurrentPiece(currentPlayer.getPiece().adjustLength(
				gui.getToolPanel().getEffectsTool().getLengthValue()
			));
		} else if(command.equals(RESET_LENGTH)) {
			//Reset the Length shown in the ToolPanel to the current length.
			gui.getToolPanel().getEffectsTool().setLengthValue(
				currentPlayer.getPiece().getLength()
			);
		}
	}

	/**
	 * Parses the given file to create a Piece. The File should be formatted in
	 * the correct way otherwise errors may be created.<br>
	 * To see the format of a Piece File, see savePiece in SavePieceListener.
	 * @param file The file to parse
	 * @return The Piece
	 */
	public Piece loadPiece(File file) {
		String data;
		try {
			Scanner sc = new Scanner(file);
			data = sc.nextLine();
		} catch(IOException ioe) {
			System.out.println("File cound't be found.");
			return Piece.createNewPiece("File Error", 20);
		}

		String[] splitted = data.split(";");

		String[] details = splitted[0].split("\\+");
		String pieceName = details[0];
		int pieceTempo = Integer.parseInt(details[1]);
		int pieceLength = Integer.parseInt(details[2]);

		List<Sequence> sequences = new ArrayList<>();

		String[] sequenceStrings = splitted[1].split("\\+");
		for(int i = 0; i < sequenceStrings.length - 1; i++) {
			String[] sequenceSplit = sequenceStrings[i].split(":");

			Instrument instrument = Instrument.valueOf(sequenceSplit[0]);
			int volume = Integer.parseInt(sequenceSplit[1]);

			String[] noteStrings = sequenceSplit[2].split("\\.");
			int[] notes = new int[noteStrings.length];
			for(int j = 0; j < notes.length; j++) {
				notes[j] = Integer.parseInt(noteStrings[j]);
			}

			sequences.add(new Sequence(instrument, notes, volume));
		}

		return new Piece(pieceName, sequences, pieceLength, pieceTempo);

	}

	/**
	 * Switches the current piece in the GUI to the given piece
	 * @param newPiece The new piece to show.
	 */
	private void changeCurrentPiece(Piece newPiece) {

		EditorPanel editorPanel = gui.getEditorPanel();
		editorPanel.hideSelectedTrackPanel();

		if(currentPlayer != null) {
			//Clear any dependants and inputs from previous player
			AudioContext ac = currentPlayer.getAudioContext();
			ac.out.clearDependents();
			ac.out.clearInputConnections();
			//Remove previous sequences
			editorPanel.removeSequences(0, currentPlayer.getPiece().getSequenceCount());
			currentPlayer = new Player(newPiece, ac);
		} else {
			//Otherwise create a new player with a new AudioContext
			currentPlayer = new Player(newPiece);
		}

		//Set Name
		gui.changePieceName(newPiece.getName());

		//Set up EditorPanel
		List<Sequence> sequences = newPiece.getSequences();
		for(Sequence sequence : sequences) {
			//Set up instrument button
			SequenceTool sequenceTool = new SequenceTool(sequence.getVolume());
			sequenceTool.addSequenceChangeListener(
				new SequenceChangeListener(newPiece, sequence, gui)
			);
			InstrumentButton instrumentButton = new InstrumentButton(
				sequence.getInstrument(), sequenceTool
			);

			//Set up track
			TrackPanel trackPanel = new TrackPanel(sequence.getAllNotes());
			trackPanel.setTrackChangeListener(new TrackModifiedListener(currentPlayer, sequence));

			editorPanel.addSequence(instrumentButton, trackPanel);
		}

		//Set tempo slider
		EffectsTool effectsTool = gui.getToolPanel().getEffectsTool();
		effectsTool.setTempoValue(newPiece.getTempo());
		effectsTool.setLengthValue(newPiece.getLength());

		//Set save/convert actions to save this new piece
		gui.getToolPanel().getOptionsTool().setSavePieceListener(
			new SavePieceListener(newPiece, gui)
		);

		//Pause the controls
		gui.getMenuPanel().setState(PlaybackState.PAUSE);

		//Reset seeker and set its length to match new piece
		JSlider seeker = editorPanel.getSeeker();
		seeker.setMaximum(newPiece.getLength());
		seeker.setValue(0);


		//Change listeners to they affect the new piece instead of the old one.
		gui.setPlayerControlListener(new PlayerControlListener(currentPlayer, gui));
		gui.setAddSequenceListener(new AddSequenceListener(currentPlayer, gui));
		gui.setNameChangeListener(new NameChangeListener(newPiece, gui));
		gui.setTempoChangeListener(new TempoChangeListener(currentPlayer));

		currentPlayer.startAudioContext(); //Start
	}

}
